import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Random;


public class BadugiServer extends Thread {
		private ServerSocket serverSocket = null;
		private int port;
		private Hashtable<Socket, DataOutputStream> outputStreams = new Hashtable<Socket, DataOutputStream>();
		boolean allowNewPlayers = true;
		private Socket[] sockets = new Socket[6];
		private DataInputStream reader[] = new DataInputStream[6];
		private DataOutputStream writer[] = new DataOutputStream[6];
		
		private int currentPlayerId = 0;
		int numberOfCurrentPlayers = 0, numberOfAllInPlayers = 0;
		int numberOfPlayers = 0, numberOfChips = 0, smallBlind = 0, bigBlind = 0;
		int dealerButtonHolder = 0, smallBlindHolder = 0, bigBlindHolder = 0;
		
		boolean gameStarted = false;
		private boolean ar;
		boolean playerStatus[] = null;
		
		int playerChips[] = null;
		
		String[] playerCards = null;
		String[][] playerMoves;
		private String playerName;
		
		int[] betsAllGame;
		int[] betsThisRound;
		int movesThisRound = 0, maxBetHolder = 0, pot = 0,  currentRoundPot = 0, currentRound = 1;

		String[] currentDeck = new String[52], putDeck = new String[0], players;
		
		public BadugiServer(int port, int numberOfPlayers) throws IOException {
			this.port = port;
			
			try {
				serverSocket = new ServerSocket(port);
			} catch (Exception e) {
				System.out.println(" > Cannot connect to port " + port + "!\n   Choose another port.\n");
				System.exit(-1);
			}
			
			this.numberOfPlayers = numberOfPlayers;
			this.numberOfChips = numberOfChips;
			this.smallBlind = smallBlind;
			this.bigBlind = bigBlind;
			
			players = new String[numberOfPlayers];
			playerCards = new String[numberOfPlayers*4];
			playerChips = new int[numberOfPlayers];
			playerStatus = new boolean[numberOfPlayers];
			playerMoves = new String[numberOfPlayers][2];
			betsThisRound = new int[numberOfPlayers];
			betsAllGame = new int[numberOfPlayers];
					
			currentDeck = newDeckOfCards();
			currentDeck = shuffleDeck(currentDeck);
			
			handOfCards(numberOfPlayers);
			allocateChips(numberOfPlayers, numberOfChips);
			setPlayersStatus(numberOfPlayers);
			
			
			try {
				listen();
			} catch (Exception e) {
				System.exit(-1);
			}
			System.out.println("\n > Bot successfully connected to server :)\n");

			for (int i = 0; i < numberOfPlayers; i++) {
				reader[i] = new DataInputStream(sockets[i].getInputStream());
				writer[i] = new DataOutputStream(sockets[i].getOutputStream());
				writer[i].writeUTF("welcomeInGame");
			}			
	
			start();
		}
		

		private void listen() throws IOException {
			System.out.println(" > Server is ready.");
			System.out.println(" > Waiting for players.\n");

	     	while (allowNewPlayers) {
	     		if (numberOfCurrentPlayers >= numberOfPlayers) {
	     			continue;  
	     		}    			    			
	     		
	     		Socket playerSocket = serverSocket.accept();     			
	     		currentPlayerId = 0;     		
	     		numberOfCurrentPlayers += 1;

	     		System.out.println(" > New player joined the game.\n   Hello, player no. " + numberOfCurrentPlayers +" "+playerSocket+ "\n");

	     		DataOutputStream stream = new DataOutputStream(playerSocket.getOutputStream());

	     		outputStreams.put(playerSocket, stream);	     		
	     		sockets[numberOfCurrentPlayers-1] = playerSocket;	
	     		
	     		if (numberOfCurrentPlayers == numberOfPlayers) {
	     			allowNewPlayers = false;
	     		}
	     		
	     	}
		}
		
		public void run() {	
			try {
				while (true) {	
					String response[] = new String[numberOfPlayers];
					
					boolean b = true;
					int l = 0;
					while (b) {
						response[l] = reader[l].readUTF();
						if (response[l] == null) {
							l++;
						} else {
							String[] tab = response[l].split(" ");
							String cmd = tab[0];
		
							if (cmd.equalsIgnoreCase("returnMyID")) {
								writer[l].writeUTF("yourID " + (l+1));
								
							} 
							if (cmd.equalsIgnoreCase("setPlayerName")) {
								try {

									if (tab[1] == null || tab[1].equalsIgnoreCase("null"))
										playerName = "Player-" + (l+1);
									else
										playerName = validatePlayerName(tab[1]);

								} catch (Exception e) {
									playerName = "Player-" + (l+1);
								}
								
								writer[l].writeUTF("yourName " + playerName);
								players[l] = playerName;
								sendToAll(toColor("&nbsp; New player: " + toBold(playerName), "#00b4ff"));
								sendToAll("tableUpdate newPlayer " + (l+1) + " " + playerName);
								
								String str = "";
								
								for (int i = 0; i < numberOfPlayers; i++) {
									if (playerStatus[i] == true)
										str += " " + (i+1) + " " + players[i];
								}
								
								writer[l].writeUTF("tableUpdate allCurrentPlayers" + str);
								
							} else if (cmd.equalsIgnoreCase("returnMyCards")) {
								String cards = "";
								int firstCardIndex = (l) * 4;
									
								for (int i = firstCardIndex; i <= firstCardIndex + 3; i++)
									cards += playerCards[i] + " ";
								
								writer[l].writeUTF("yourCards " + cards);
								
							} else if (cmd.equalsIgnoreCase("returnMyChipsOnRoundStart")) {
								writer[l].writeUTF("yourChipsOnRoundStart " + playerChips[l]);
								
							} else if (cmd.equalsIgnoreCase("returnMyAward")) {
								playerChips[l] += pot;
								pot = 0;
								writer[l].writeUTF("chipsWithAward " + playerChips[l]);
								
							} else if (cmd.equalsIgnoreCase("numberOfPlayers")) {
								writer[l].writeUTF("numberOfPlayers " + numberOfPlayers);	
								
							} else if (cmd.equalsIgnoreCase("dontDrawMyCards")) {
								registerPlayerMove(l+1, "dontDrawMyCards", null);
								writer[l].writeUTF("dontDrawCardsSuccess");
								
								String[] roundStatus = isEndOf(0);
								
								sendToAll(toColor("&nbsp; Player " + toBold(playerName) + " didn't draw any card.", "#9f9696"));
								
								
								if (roundStatus[0].equals("true")) {
									if (roundStatus[1].equals("true")) {
										sendToAll("endOfGame");
										chooseWinner();
									} else {
										sendToAll("endOfRound " + roundStatus[2]);
										sendToAll("newRound " + (Integer.parseInt(roundStatus[2])+1));
										sendToAll("nextPlayerMove " + smallBlindHolder + " " + players[smallBlindHolder-1] + " " + possiblePlayerMoves(smallBlindHolder));
									}
								}				
								
							} else if (cmd.equalsIgnoreCase("drawMyCards")) {
								registerPlayerMove(l+1, "drawMyCards", null);
								drawCards(l+1, response[l]);
								
								String cards = "";
								int firstCardIndex = l * 4;
									
								for (int i = firstCardIndex; i <= firstCardIndex + 3; i++)
									cards += playerCards[i] + " ";
								
								writer[l].writeUTF("drawCardsSuccess");
								writer[l].writeUTF("yourCards " + cards);
								
								String[] roundStatus = isEndOf(0);

								
								if (tab.length-1 > 1)
									sendToAll(toColor("&nbsp; Player " + toBold(playerName) + " drew " + (tab.length-1) + " cards.", "#9f9696"));
								else
									sendToAll(toColor("&nbsp; Player " + toBold(playerName) + " drew 1 card.", "#9f9696"));
								
								
								if (roundStatus[0].equals("true")) {
									if (roundStatus[1].equals("true")) {
										sendToAll("endOfGame");
										chooseWinner();
									} else {
										sendToAll("endOfRound " + roundStatus[2]);
										sendToAll("newRound " + (Integer.parseInt(roundStatus[2])+1));
										sendToAll("nextPlayerMove " + smallBlindHolder + " " + players[smallBlindHolder-1] + " " + possiblePlayerMoves(smallBlindHolder));
									}
								}					
								
							} else if (cmd.equalsIgnoreCase("returnMyCards")) {
								String cards = "";
								int firstCardIndex = l * 4;
									
								for (int i = firstCardIndex; i <= firstCardIndex + 3; i++)
									cards += playerCards[i] + " ";
								
								writer[l].writeUTF("yourCards " + cards);
								
							} else if (cmd.equalsIgnoreCase("returnMyChips")) {
								writer[l].writeUTF("yourChips " + playerChips[l]);
								
								/* Warunek rozpoczecia gry */
								if (numberOfCurrentPlayers == numberOfPlayers && !gameStarted) {
									gameStarted = true;
									letsTheGameBegin();
								}	
								
							} else if (cmd.equalsIgnoreCase("check")) {
								ar = registerPlayerMove(l+1, "check", null);
								
								if (ar) {
									writer[l].writeUTF("actionOk");
									sendToAll("tableUpdate playerBet " + l+1 + " " + betsThisRound[l]);
								} else {
									writer[l].writeUTF("actionError");
									continue;
								}
								
								sendToAll(toColor("&nbsp; Player " + toBold(playerName) + " played <i>Check</i>.", "white"));

								int nextPlayerID = nextPlayer(l+1);
								String[] roundStatus = isEndOf(nextPlayerID);
								
								
								if (roundStatus[0].equals("true")) {
									if (roundStatus[1].equals("true")) {
										sendToAll("endOfGame");
										chooseWinner();
									} else {
										sendToAll("endOfRound " + roundStatus[2]);
										sendToAll("newRound " + (Integer.parseInt(roundStatus[2])+1));
									}
								} else {
									sendToAll("nextPlayerMove " + nextPlayerID + " " + players[nextPlayerID-1] + " " + possiblePlayerMoves(nextPlayerID));
								}
								
							} else if (cmd.equalsIgnoreCase("bet")) {
								ar = registerPlayerMove(l+1, "bet", tab[1]);
								
								if (ar) {
									writer[l].writeUTF("actionOk");
									writer[l].writeUTF("tableUpdate yourBet " + betsAllGame[l]);	
									sendToAll("tableUpdate playerBet " + l+1 + " " + betsThisRound[l]);
								} else {
									writer[l].writeUTF("actionError");
									continue;
								}
								
								sendToAll(toColor("&nbsp; Player " + toBold(playerName) + " played <i>Bet ($" + tab[1] + ")</i>.", "white"));
								
								int nextPlayerID = nextPlayer(l+1);
								String[] roundStatus = isEndOf(nextPlayerID);
								
								
								if (roundStatus[0].equals("true")) {
									if (roundStatus[1].equals("true")) {
										sendToAll("endOfGame");
										chooseWinner();
									} else {
										sendToAll("endOfRound " + roundStatus[2]);
										sendToAll("newRound " + (Integer.parseInt(roundStatus[2])+1));
									}
								} else {
									sendToAll("nextPlayerMove " + nextPlayerID + " " + players[nextPlayerID-1] + " " + possiblePlayerMoves(nextPlayerID));
								}
								
							} else if (cmd.equalsIgnoreCase("raise")) {
								ar = registerPlayerMove(l+1, "raise", tab[1]);
								
								if (ar) {
									writer[l].writeUTF("actionOk");
									writer[l].writeUTF("tableUpdate yourBet " + betsAllGame[l]);	
									sendToAll("tableUpdate playerBet " + l+1 + " " + betsThisRound[l]);
								} else {
									writer[l].writeUTF("actionError");
									continue;
								}
								
								sendToAll(toColor("&nbsp; Player " + toBold(playerName) + " played <i>Raise ($" + tab[1] + ")</i>.", "white"));

								int nextPlayerID = nextPlayer(l+1);
								String[] roundStatus = isEndOf(nextPlayerID);


								if (roundStatus[0].equals("true")) {
									if (roundStatus[1].equals("true")) {
										sendToAll("endOfGame");
										chooseWinner();
									} else {
										sendToAll("endOfRound " + roundStatus[2]);
										sendToAll("newRound " + (Integer.parseInt(roundStatus[2])+1));
									}
								} else {
									sendToAll("nextPlayerMove " + nextPlayerID + " " + players[nextPlayerID-1] + " " + possiblePlayerMoves(nextPlayerID));
								}

							} else if (cmd.equalsIgnoreCase("call")) {
								ar = registerPlayerMove(l+1, "call", null);
								
								if (ar) {
									writer[l].writeUTF("actionOk");
									writer[l].writeUTF("tableUpdate yourBet " + betsAllGame[l]);	
									sendToAll("tableUpdate playerBet " + l+1 + " " + betsThisRound[l]);
								} else {
									writer[l].writeUTF("actionError");
									continue;
								}
								
								sendToAll(toColor("&nbsp; Player " + toBold(playerName) + " played <i>Call</i>.", "white"));

								int nextPlayerID = nextPlayer(l+1);
								String[] roundStatus = isEndOf(nextPlayerID);


								if (roundStatus[0].equals("true")) {
									if (roundStatus[1].equals("true")) {
										sendToAll("endOfGame");
										chooseWinner();
									} else {
										sendToAll("endOfRound " + roundStatus[2]);
										sendToAll("newRound " + (Integer.parseInt(roundStatus[2])+1));
									}
								} else {
									sendToAll("nextPlayerMove " + nextPlayerID + " " + players[nextPlayerID-1] + " " + possiblePlayerMoves(nextPlayerID));
								}

							} else if (cmd.equalsIgnoreCase("allIn")) {
								ar = registerPlayerMove(l+1, "allIn", null);
								
								if (ar) {
									writer[l].writeUTF("actionOk");
									writer[l].writeUTF("tableUpdate yourBet " + betsAllGame[l]);	
									sendToAll("tableUpdate playerBet " + l+1 + " " + betsThisRound[l]);
									sendToAll("tableUpdate allIn " + l+1);
								} else {
									writer[l].writeUTF("actionError");
									continue;
								}
								
								sendToAll(toColor("&nbsp; Player " + toBold(playerName) + " played <i>All-in</i>.", "white"));

								int nextPlayerID = nextPlayer(l+1);
								String[] roundStatus = isEndOf(nextPlayerID);


								if (roundStatus[0].equals("true")) {
									if (roundStatus[1].equals("true")) {
										sendToAll("endOfGame");
										chooseWinner();
									} else {
										sendToAll("endOfRound " + roundStatus[2]);
										sendToAll("newRound " + (Integer.parseInt(roundStatus[2])+1));
									}
								} else {
									sendToAll("nextPlayerMove " + nextPlayerID + " " + players[nextPlayerID-1] + " " + possiblePlayerMoves(nextPlayerID));
								}

							} else if (cmd.equalsIgnoreCase("fold")) {
								ar = registerPlayerMove(l+1, "fold", null);
								
								if (ar) {
									writer[l].writeUTF("actionOk");
								}
								
								writer[l].writeUTF("disableMoves");
								sendToAll(toColor("&nbsp; Player " + toBold(playerName) + " left the game :(", "red"));
								sendToAll("tableUpdate fold " + l+1);
								
								int nextPlayerID = nextPlayer(l+1);
								String[] roundStatus = isEndOf(nextPlayerID);


								if (roundStatus[0].equals("true")) {
									if (roundStatus[1].equals("true")) {
										sendToAll("endOfGame");
										chooseWinner();
									} else {
										sendToAll("endOfRound " + roundStatus[2]);
										sendToAll("newRound " + (Integer.parseInt(roundStatus[2])+1));
									}
								} else {
									sendToAll("nextPlayerMove " + nextPlayerID + " " + players[nextPlayerID-1] + " " + possiblePlayerMoves(nextPlayerID));
								}
							}
							
							l++;
							if (l == numberOfPlayers) {
								b = false;
							}
						}
					}
				}
				
			} catch (Exception e) {
				System.out.println("Something wrong happens..");
			}
				
		}
		
		void sendToAll(String message) {
			synchronized (outputStreams) {

				for (Enumeration en = outputStreams.elements(); en.hasMoreElements();) {
					DataOutputStream writer = (DataOutputStream)en.nextElement();
	       
					try {
						writer.writeUTF(message);
					} catch(IOException e) { 
						System.out.println(e.getMessage());
					}
				}
			}
		}
		
		private void letsTheGameBegin() {
			System.out.println("\n > Start of the game.\n");
			
			allowNewPlayers = false;
			
			setDealerButtonHolder();
			setBlindsHolders();
			sendToAll("startOfTheGame " + toColor(toBold("START OF THE GAME"),"orange"));
			
			
			int playerMoveId = nextPlayer(bigBlindHolder);
			String data;
			
			data = "startData ";
			data += "playerMoveId " + playerMoveId;
			data += " playerMoveName " + players[playerMoveId-1];
			data += " dealer " + dealerButtonHolder;
			data += " " + smallBlind + " " + smallBlindHolder;
			data += " " + bigBlind + " " + bigBlindHolder;
			data += " " + possiblePlayerMoves(playerMoveId);

			sendToAll("&nbsp; Player <b>" + players[smallBlindHolder-1] + "</b> bet <i>small blind ($" + smallBlind + ")</i>.");
			sendToAll("&nbsp; Player <b>" + players[bigBlindHolder-1] + "</b> bet <i>big blind ($" + bigBlind + ")</i>.");
			sendToAll("tableUpdate potAndMaxBet " + pot + " " + bigBlind);
			sendToAll(data);
		}
		
		
		/** Check if end of current round/ game. **/
		String[] isEndOf(int pID) {
			if (movesThisRound < numberOfCurrentPlayers + numberOfAllInPlayers && currentRound % 2 == 0)
				return new String[]{"false", "false"};
			else if (movesThisRound < numberOfCurrentPlayers)
				return new String[]{"false", "false"};
			else {
				if (currentRound % 2 == 1) {
					int prevVal = -1;
					
					for (int i = 0; i < numberOfPlayers; i++) {
						if (playerMoves[i][0] != null && !playerMoves[i][0].equals("null")) {
							if (!playerMoves[i][0].equals("allIn") && !playerMoves[i][0].equals("fold")) {
								if (betsThisRound[i] != prevVal && prevVal > -1) {
									return new String[]{"false", "false"};
								} else if (prevVal == -1) {
									prevVal = betsThisRound[i];
								}	
							}
						}
					}
					
					
					if (currentRound == 7) {
						String allCardsStr = "";
						
						for (int i = 0; i < numberOfPlayers; i++) {
							allCardsStr += " " + players[i];
							
							String[][] c = sortCardsWithoutSplice(new String[][]{
									playerCards[4*i].split("-"),
									playerCards[4*i+1].split("-"),
									playerCards[4*i+2].split("-"),
									playerCards[4*i+3].split("-")});
							
							for (int j = 0; j <= 3; j++)
								allCardsStr += " " + c[j][0] + "-" + c[j][1] + "-" + c[j][2];
						}
						
						sendToAll("tableUpdate showCards" + allCardsStr);
						return new String[]{"true", "true"};
					} else {
						betsThisRound = new int[numberOfPlayers];
						playerMoves = new String[numberOfPlayers][2];
						movesThisRound = 0;
						currentRoundPot = 0;
						maxBetHolder = 0;
						
						currentRound += 1;
						
						return new String[]{"true", "false", (currentRound-1) + ""};
					}
				} else {
					betsThisRound = new int[numberOfPlayers];
					playerMoves = new String[numberOfPlayers][2];
					movesThisRound = 0;
					currentRoundPot = 0;
					maxBetHolder = 0;
					
					currentRound += 1;
					
					setDealerButtonHolder();
					setBlindsHolders();
					
					sendToAll("tableUpdate dealerAndBlinds " + dealerButtonHolder + " " + smallBlindHolder + " " + bigBlindHolder);
					
					return new String[]{"true", "false", (currentRound-1) + ""};
				}
			}
		}
		
		private String toBold(String str) {
			return "<b>" + str + "</b>";
		}
		
		private String toColor(String str, String color) {
			return "<span style=\"color:" + color + ";\">" + str + "</span>";
		}
		
		
		/** Return possible (next) player moves. **/
		String possiblePlayerMoves(int pID) {
			if (currentRound == 1) {
				
				if (playerChips[pID-1] > 0) {
					int playerBet = playerChips[pID-1] + betsThisRound[pID-1];
					int maxBet = maxBetThisRound();
					
					if (pID == bigBlindHolder && maxBet <= bigBlind) {
					
						if (playerBet > maxBet)
							return "true false false true true";
						else
							return "true false false false true";
					} else {

						if (playerBet > maxBet && pID != maxBetHolder)
							return "false false true true true";
						else if (playerBet == maxBet && pID != maxBetHolder)
							return "false false true false true";
						else
							return "false false false false true";
					}

				} else {
					return "false false false false false";
				}
				
			} else {
				if (currentRoundPot == 0) {

					if (playerChips[pID-1] > 0) {
						return "true true false false true";
					} else {
						return "true true false false false";
					}

				} else {
					if (playerChips[pID-1] > 0) {
						int playerBet = playerChips[pID-1] + betsThisRound[pID-1];
						int maxBet = maxBetThisRound();
						
						
						if (playerBet > maxBet && pID != maxBetHolder)
							return "false false true true true";
						else if (playerBet == maxBet && pID != maxBetHolder)
							return "false false true false true";
						else
							return "false false false false true";
					} else {
						return "false false false false false";
					}
				}
			}
		}
		
		
		/** Checks and remembers player move. **/
		boolean registerPlayerMove(int pID, String action, String amount) {
			if (!action.equalsIgnoreCase("drawCards") && !action.equalsIgnoreCase("dontDrawMyCards")) {
				
				int amountInt;

				try {
					amountInt = Integer.parseInt(amount);
				} catch (Exception e) {
					amountInt = 0;
				}
				
				if (action.equals("bet")) {
					if (amountInt >= playerChips[pID-1])
						return false;
					
					pot += amountInt;
					betsThisRound[pID-1] += amountInt;
					betsAllGame[pID-1] += amountInt;
					playerChips[pID-1] -= amountInt;
					currentRoundPot += amountInt;
					maxBetHolder = pID;
					movesThisRound += 1;

					sendToAll("tableUpdate potAndMaxBet " + pot + " " + maxBetThisRound());

				} else if (action.equals("raise")) {
					int maxBet = maxBetThisRound();
					int thisMoveBet = maxBet + amountInt - betsThisRound[pID-1];
					
					if (thisMoveBet >= playerChips[pID-1])
						return false;

					pot += thisMoveBet;
					betsThisRound[pID-1] = maxBet + amountInt;
					betsAllGame[pID-1] += thisMoveBet;
					playerChips[pID-1] -= thisMoveBet;
					currentRoundPot += thisMoveBet;
					maxBetHolder = pID;
					movesThisRound += 1;

					sendToAll("tableUpdate potAndMaxBet " + pot + " " + maxBetThisRound());
					
				} else if (action.equals("call")) {
					int maxBet = maxBetThisRound();
					int thisMoveBet = maxBet - betsThisRound[pID-1];
					
					if (thisMoveBet >= playerChips[pID-1])
						return false;

					pot += thisMoveBet;
					betsThisRound[pID-1] = maxBet;
					betsAllGame[pID-1] += thisMoveBet;
					playerChips[pID-1] -= thisMoveBet;
					currentRoundPot += thisMoveBet;
					maxBetHolder = pID;
					movesThisRound += 1;

					sendToAll("tableUpdate potAndMaxBet " + pot + " " + maxBetThisRound());
					
				} else if (action.equals("allIn")) {
					int allPlayerChips = playerChips[pID-1];
					
					pot += allPlayerChips;
					betsThisRound[pID-1] += allPlayerChips;
					betsAllGame[pID-1] += allPlayerChips;
					playerChips[pID-1] = 0;
					playerStatus[pID-1] = false;
					numberOfCurrentPlayers -= 1;
					numberOfAllInPlayers += 1;

					sendToAll("tableUpdate potAndMaxBet " + pot + " " + maxBetThisRound());
					
					if (numberOfCurrentPlayers <= 1) {
						currentRound = 7;
						movesThisRound = 1;
					}
				} else if (action.equals("fold")) {
					playerChips[pID-1] = 0;
					playerStatus[pID-1] = false;
					players[pID-1] = null;
					numberOfCurrentPlayers -= 1;
					
					if (numberOfCurrentPlayers <= 1) {
						currentRound = 7;
						movesThisRound = 1;
					}
				} else {
					movesThisRound += 1;
				}
				
				
				playerMoves[pID-1] = new String[]{action, amount};
				return true;
			} else {
				
				movesThisRound += 1;
				return true;
			}
		}
		
		
		/** Returns a max bet in current round. **/
		int maxBetThisRound() {
			try {
				return betsThisRound[maxBetHolder-1];
			} catch (Exception e) {
				return 0;
			}
		}
		
		
		/** Checks if new player's name is unique. **/
		String validatePlayerName(String playerName) {
			boolean tmp = true;
			
			while (tmp) {
				tmp = false;
				
				for (int i = 0; i < players.length; i++) {
					if (playerName.equalsIgnoreCase(players[i])) {
						playerName += ".1";
						tmp = true;
					}
				}
			}
			
			return playerName;
		}
	  

		/** Sets next player, who has move. **/
		int nextPlayer(int ID) {
			if (numberOfCurrentPlayers == 1)
				return ID;
			
			int nextPlayerID = ID % numberOfPlayers + 1;
			
			while (playerStatus[nextPlayerID-1] == false) {
				nextPlayerID = nextPlayerID % numberOfPlayers + 1;
				
			}
			
			return nextPlayerID;
		}
		
		
		/** Sets, who has a dealer button. **/
		void setDealerButtonHolder() {
			if (currentRound == 1) {
				Random rand = new Random();
				dealerButtonHolder = rand.nextInt(numberOfPlayers) + 1;
			} else {
				dealerButtonHolder = nextPlayer(dealerButtonHolder);
			}
		}
		

		/** Sets, which players owns small and big blind. **/
		void setBlindsHolders() {
			smallBlindHolder = nextPlayer(dealerButtonHolder);
			bigBlindHolder = nextPlayer(smallBlindHolder);
			
			if (currentRound == 1) {
				playerChips[smallBlindHolder-1] -= smallBlind;
				playerChips[bigBlindHolder-1] -= bigBlind;

				pot += smallBlind + bigBlind;
				currentRoundPot += smallBlind + bigBlind;
				
				betsThisRound[smallBlindHolder-1] = smallBlind;
				betsThisRound[bigBlindHolder-1] = bigBlind;
				
				betsAllGame[smallBlindHolder-1] = smallBlind;
				betsAllGame[bigBlindHolder-1] = bigBlind;
				
				playerMoves[smallBlindHolder-1] = new String[]{"bet", smallBlind + ""};
				playerMoves[bigBlindHolder-1] = new String[]{"raise", (bigBlind - smallBlind) + ""};
				
				maxBetHolder = bigBlindHolder;
			}
		}
		
		
		/** Sorts cards and deletes distinct. **/
		String[][] sortCardsArray(String[][] array) {
			String[][] array2 = new String[4][3];
			int j = 0, minIndex = 0;
			
			for (int i = 0; i < array.length; i++) {
				int min = 15;
				
				for (j = 0; j < array.length; j++) {
					if (array[j][0] != null) {
						if (Integer.parseInt(array[j][0]) < min) {
							min = Integer.parseInt(array[j][0]);
							minIndex = j;
						} else if (Integer.parseInt(array[j][0]) == min) {
							array[j] = new String[3];
						}
					}
				}
				
				array2[i] = array[minIndex];
				array[minIndex] = new String[3];
			}
			
			return array2;
		}

		
		/** Sorts and prepares an array of cards. **/
		int[] organizeCards(String[][] array) {

			int[] outputArray;
			
			String[][] array2 = sortCardsArray(array);
			
			String colors = "";
			int tmpCount = 0;
			
			for (int i = 0; i < array2.length; i++) {
				if (array2[i][2] == null) {
					array2[i] = new String[3];
				} else if (colors.contains(array2[i][2])) {
					array2[i] = new String[3];
				} else {
					colors += array2[i][2];
					tmpCount += 1;
				}
			}
			
			int k = 0;
			outputArray = new int[4];
			
			for (int i = 0; i < array2.length; i++) {
				if (array2[i][0] != null) {
					outputArray[k] = Integer.parseInt(array2[i][0]);
					k += 1;
				} else {
					outputArray[k] = 0;
				}
			}
			
			return outputArray;
		}
		
		/** Sorts card without deleting distincts. **/
		String[][] sortCardsWithoutSplice(String[][] array) {
			String[][] array2 = new String[4][3];
			int j = 0, minIndex = 0;
			
			for (int i = 0; i < array.length; i++) {
				int min = 15;
				
				for (j = 0; j < array.length; j++) {
					if (array[j][0] != null) {
						if (Integer.parseInt(array[j][0]) < min) {
							min = Integer.parseInt(array[j][0]);
							minIndex = j;
						}
					}
				}
				
				array2[i] = array[minIndex];
				array[minIndex] = new String[3];
			}
			
			return array2;
		}

		
		/** Chooses a winner of current game. **/
		void chooseWinner() {
			String[] allHands = new String[numberOfPlayers];
			
			for (int i = 0; i < numberOfPlayers; i++) {
				String[][] tmp = new String[4][3];
				
				if (playerStatus[i] == false && players[i] == null) {
					allHands[i] = "xxxx";
					
				} else { 
					for (int j = 0; j < 4; j++)
						tmp[j] = playerCards[i*4+j].split("-");
				
					allHands[i] = numericArrayToString(organizeCards(tmp));
				}
				
				System.out.println("   " + players[i] + "'s hand code: " + allHands[i]);
			}
			
			String cmp = allHands[0];
			int dupl = 0;
			int winnerID = 1;
			
			
			for (int i = 0; i+1 < allHands.length; i++) {
				if (cmp.compareTo(allHands[i+1]) == 0) {
					dupl += 1;
				} else if (cmp.compareTo(allHands[i+1]) > 0) {
					cmp = allHands[i+1];
					winnerID = i+2;
					dupl = 0;
				}
			}
			
			if (dupl == 0) {
				sendToAll("gameResult winner " + winnerID + " " + players[winnerID-1]);
			} else {
				sendToAll("gameResult tie");
			}
		}
		

		/** Changes an array (output from 'arraySort') to String. **/
		String numericArrayToString(int[] array) {
			String[] chars = new String[]{"x","a","b","c","d","e","f","g","h","i","j","k","l","m"};
			String output = "";
			
			for (int i = array.length-1; i >= 0 ; i--) {
				output += chars[array[i]];
			}
			
			return output;
		}
		
		
		/** Removes a connection, when 'fold' or connection error. **/
		void removeConnection(Socket playerSocket, String playerName, int playerId) {
			synchronized (outputStreams) {
				System.out.println("\n > Deleting " + playerName + "'s socket.\n");

				outputStreams.remove(playerSocket);
				playerStatus[playerId-1] = false;
				players[playerId-1] = null;
				
				
				if (allowNewPlayers)
					numberOfCurrentPlayers -= 1;
				
				try {
					playerSocket.close();
				} catch(IOException e) {
					System.out.println("\n > Error while closing " + playerName + "'s socket.\n");
				}
				
				sendToAll("<span style=\"color:red\">Player <b>" + playerName + "</b> left the game :(</span>");
			}
		}
		
		
		/** Creates a new deck of cards. **/
		String[] newDeckOfCards() {
			String[] newDeck = new String[52];
			
			//  1: As 	 [A]
			// 11: Valet [V]
			// 12: Queen [Q]
			// 13: King  [K]
			
			int[] numbers = new int[]{1,2,3,4,5,6,7,8,9,10,11,12,13};
			String[] colors = new String[]{"Red-Heart", "Red-Diamond", "Black-Spade", "Black-Club"};
			
			int index = 0;
			
			for (int i = 0; i < numbers.length; i++) {
				for (int j = 0; j < colors.length; j++) {
					newDeck[index] = numbers[i] + "-" + colors[j];
						
					index += 1;
				}
			}
			
			return newDeck;
		}
		
		
		/** Shuffles a deck of cards. **/
		String[] shuffleDeck(String[] oldDeck) {
			Random random = new Random();
			
		    for (int i = oldDeck.length - 1; i > 0; i--) {
		      int index = random.nextInt(i + 1);
		     
		      String oldIndexValue = oldDeck[index];
		      
		      oldDeck[index] = oldDeck[i];
		      oldDeck[i] = oldIndexValue;
		    }
		    
		    return oldDeck;
		}
		
		
		/** Slices an array by removing all nulls. **/
		void sliceAllNulls(String[] deck) {
			int index = 0;
			int newLength = 0;
			String[] newDeck = null;
			
			for (int i = 0; i < deck.length; i++) {
				if (deck[i] != null)
					newLength += 1;
			}
			
			newDeck = new String[newLength];
			
			for (int i = 0; i < deck.length; i++) {
				if (deck[i] != null) {
					newDeck[index] = deck[i];
					index += 1;
				}
			}
			
			currentDeck = new String[newLength];
			currentDeck = newDeck;
		}
		
		
		/** Adds a put card to put deck. **/
		void extendPutDeck(String[] deck, String card) {
			String[] newDeck = new String[deck.length+1];
			
			for (int i = 0; i < deck.length; i++)
				newDeck[i] = deck[i];
			
			newDeck[deck.length] = card;
			
			putDeck = new String[newDeck.length];
			putDeck = newDeck;
		}
		
		
		/** First hand of cards. **/
		void handOfCards(int numberOfPlayers) {
			for (int i = 0; i < numberOfPlayers*4; i++) {
				playerCards[i] = currentDeck[i];
				currentDeck[i] = null;
			}
			
			sliceAllNulls(currentDeck);
		}
		
		
		/** Gives every player the same amount of chips. **/
		void allocateChips(int numberOfPlayers, int numberOfChips) {
			for (int i = 0; i < numberOfPlayers; i++) 
				playerChips[i] = numberOfChips;
		}
		
		
		/** Sets every player status to false (is not active) on the beginning. **/
		void setPlayersStatus(int numberOfPlayers) {
			for (int i = 0; i < numberOfPlayers; i++) 
				playerStatus[i] = false;
		}
		
		
		/** When a player wants to draw a(/some) card(/s). **/
		synchronized void drawCards(int playerId, String line) {
			String[] tab = line.split(" ");
			
			for (int i = 1; i < tab.length; i++) {
				int index = Integer.parseInt(tab[i]) + (playerId-1)*4 - 1;
				
				if (currentDeck.length == 0) {
					currentDeck = shuffleDeck(putDeck);
					putDeck = new String[0];
				}
				
				// Zapamietaj stara karte
				// Pobierz nowa karte z nowej talii
				String oldCard = playerCards[index];
				String newCard = currentDeck[0];
				
				// Usun pobrana karte z talii
				// Wymien karte na nowa
				currentDeck[0] = null;
				playerCards[index] = newCard;
				
				// Usun puste miejsce po pobranej karcie z obecnej talii
				// Dopisz wymieniona karte do odlozonej talii
				sliceAllNulls(currentDeck);
				extendPutDeck(putDeck, oldCard);
			}
		}
		

	public static void main(String[] args) throws Exception {
		new BadugiServer(4444, 2);
	}
}
